/**
 * @(#)RegistrationPIC.java		1.0		18/04/2012
 */
package upm.pb4agent.common;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.List;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import upm.pb4agent.ontology.Dock;
import upm.pb4agent.ontology.DockDesignation;
import upm.pb4agent.ontology.PB4AgentOntology;
import upm.pb4agent.ontology.RaceStarted;
import upm.pb4agent.ontology.Registration;
import upm.pb4agent.team8.Common.PB4Utils;

public class RegistrationDesk extends Agent
{
	private static final long serialVersionUID = 6110888192554850312L;
	private boolean isRegistrationClosed = false;

	private Codec codec = new SLCodec();
	private Ontology ontology = PB4AgentOntology.getInstance();

	private long _startUpTime;
	
	protected void setup() 
	{
		PB4Utils.Console.WriteLine(getLocalName() + ": Enters the system");
		
		_startUpTime = new Date().getTime();
		PB4Utils.Console.WriteLine("Registration Period Starts : " +  (new SimpleDateFormat("HH:mm:ss")).format(_startUpTime));
		ScheduleRegistrationPeriod();
		
		getContentManager().registerLanguage(codec);
		getContentManager().registerOntology(ontology);

		PB4Utils.TryRegisterAgent(this, Constant.REGISTRATION_DESK);
		addBehaviour(new ProcessingRegistration(this));
		
	}

	public class ProcessingRegistration extends SimpleBehaviour
	{
		private static final long serialVersionUID = -5444735014847500036L;
		private Agent _agent;
		
		public ProcessingRegistration(Agent agent)
		{
			super(agent);
			_agent=agent;
		}

		@Override
		public void action() 
		{
			Long currentTime = new Date().getTime();
			boolean isRegistrationFormValid = true;
			String teamID = "";

			ACLMessage aclMessage = receive();
			if (aclMessage != null)
			{
				if (aclMessage.getPerformative() == ACLMessage.REQUEST)
				{
					ContentElement contentElement = PB4Utils.GetMessageContent(_agent, aclMessage);
					if (contentElement != null && contentElement instanceof Action)
					{
						Action action = (Action) contentElement;
						Concept concept = action.getAction();

						if (concept instanceof Registration)
						{
							Registration registration = (Registration) concept;
							PB4Utils.Console.WriteLine("Arrived Registration from " + aclMessage.getSender().getLocalName());
							teamID = registration.getRegistrationForm().getTeamID().getValue();

							// Check if registration is already closed
							if (currentTime - _startUpTime >= Constant.REGISTRATION_PERIOD)
							{
								isRegistrationClosed = true;
								PB4Utils.Console.WriteLine("Registration Period Closed");
							} 
							else
							{
								// Validate the registration form
								isRegistrationFormValid = validateRegistrationForm(registration.getRegistrationForm()
										.getDockDesignation());

								if (isRegistrationFormValid)
								{
									PB4Utils.TryCreateAgent(_agent, "upm.pb4agent.common.Watcher", Constant.WATCHER + teamID, new Object[]{registration});
									PB4Utils.TrySendMessage(ACLMessage.INFORM, _agent, aclMessage.getSender() ,"");
									PB4Utils.Console.WriteLine(teamID + " registration accepted");
								}
							}
						} 
						else 
						{
							isRegistrationFormValid = false;
						}
					} else 
					{
						isRegistrationFormValid = false;
					}

					if (isRegistrationClosed == true || isRegistrationFormValid == false)
					{
						PB4Utils.TrySendMessage(ACLMessage.REFUSE, myAgent, aclMessage.getSender(), "");
						PB4Utils.Console.WriteLine(teamID + "is disqualified");
					}
				}
				else
				{
					// Message format is wrong
					PB4Utils.TrySendMessage(ACLMessage.NOT_UNDERSTOOD, myAgent, aclMessage.getSender(), "");
				}
			}
		}

		@Override
		public boolean done() {
			return isRegistrationClosed;
		}
		
		//We assume the Watcher is already created. No body will re-send the message
		private void NotifyWatcherTeamSuccessfullRegistration( String teamID, Concept concept)
		{
			AID watcherAID = PB4Utils.TryFindAgent(_agent,Constant.WATCHER);
			if ( watcherAID!=null)
			{
				PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, _agent, watcherAID, concept);
			}
		}
		/**
		 * validateRegistrationForm
		 * 
		 * @param dockDesignationList
		 * @return true if the registration form is valid with criteria: there
		 *         is no duplicate rower ID and no duplicate dock
		 */
		private boolean validateRegistrationForm(List dockDesignationList) {

			// Variable declaration
			boolean isValid = true;
			List rowerIDList = new ArrayList();
			List dockNameList = new ArrayList();
			AID rowerID = null;
			Dock dock = null;
			Iterator iterator = dockDesignationList.iterator();

			while (iterator.hasNext() == true) {
				DockDesignation dockDesignation = (DockDesignation) iterator
						.next();
				rowerID = dockDesignation.getRowerID();
				dock = dockDesignation.getDock();

				// Check if the ID is already in the list
				if (rowerIDList.contains(rowerID) == true) {
					isValid = false;
					break;
				} else {
					rowerIDList.add(rowerID);
				}

				// Check if the dock is already in the list
				if (dockNameList.contains(dock.getName()) == true) {
					isValid = false;
					break;
				} else {
					dockNameList.add(dock.getName());
				}
			}
			return isValid;
		}
	
	}

	private void NotifyRefereeRaceStartedEvent()
	{
		AID refereeAID = PB4Utils.TryFindAgent(this, Constant.REFEREE);
		if (refereeAID!=null)
		{
			PB4Utils.TrySendConceptMessage(ACLMessage.INFORM, this, refereeAID, new RaceStarted());
		}
	}
	
	public class RegistrationPeriodRemindTask extends TimerTask
	{
        public void run()
        {
            PB4Utils.Console.WriteLine("Registration Period Closed : "+ (new SimpleDateFormat("HH:mm:ss")).format(new Date().getTime()));
            ScheduleRaceStart();
            isRegistrationClosed = true;
        }
	}
	
	public class RaceStartedRemindTask extends TimerTask
	{
        public void run()
        {
            PB4Utils.Console.WriteLine("Race Started!!!! : " + (new SimpleDateFormat("HH:mm:ss")).format(new Date().getTime()));
            NotifyRefereeRaceStartedEvent();
        }
	}
	
	private void ScheduleRegistrationPeriod()
	{
		Timer timer = new Timer();
		timer.schedule(new RegistrationPeriodRemindTask(), Constant.REGISTRATION_PERIOD);
	}

	private void ScheduleRaceStart()
	{
		Timer timer = new Timer();
		timer.schedule(new RaceStartedRemindTask(), Constant.START_RACE_PERIOD);
	}

}
